package com.mf.basic.utils;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.mf.basic.enums.EnumDateFormat;
import com.mf.basic.exceptions.MyException;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class UtilsDate {

    /**
     * yyyy-MM-dd
     */
    public static final ThreadLocal<SimpleDateFormat> sdf_yyyy_MM_dd = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd");
        }
    };

    /**
     * yyyy年M日d
     */
    public static final ThreadLocal<SimpleDateFormat> sdf_yyyy_M_d = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy年M月d日");
        }
    };

    /**
     * yyMMdd
     */
    public static final ThreadLocal<SimpleDateFormat> sdf_yyMMdd = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyMMdd");
        }
    };

    /**
     * yyyy-MM-dd HH:mm:ss
     */
    public static final ThreadLocal<SimpleDateFormat> sdf_yyyy_MM_dd_HH_mm_ss = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };

    /**
     * yyyy-MM-dd HH:mm:ss
     */
    public static final ThreadLocal<SimpleDateFormat> sdf_yyyyMMddHHmmssSSS = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyyMMddHHmmssSSS");
        }
    };

    /**
     * MM-dd HH:mm
     */
    public static final ThreadLocal<SimpleDateFormat> sdf_MM_dd_HH_mm = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("MM-dd HH:mm");
        }
    };

    /**
     * yyyy-MM-dd HH:mm
     */
    public static final ThreadLocal<SimpleDateFormat> sdf_yyyy_MM_dd_HH_mm = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm");
        }
    };

    /**
     * HH:mm
     */
    public static final ThreadLocal<SimpleDateFormat> sdf_HH_mm = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("HH:mm");
        }
    };

    /**
     * HHmm
     */
    public static final ThreadLocal<SimpleDateFormat> sdf_HHmm = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("HHmm");
        }
    };

    /**
     * HHmm
     */
    public static final ThreadLocal<SimpleDateFormat> sdf_yyMM = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyMM");
        }
    };


    /**
     * 得到当前时间戳
     *
     * @return Long
     */
    public static Long getNowTimestamp() {
        return getTimestampByStr(null);
    }

    /**
     * 得到当前时间戳，含毫秒
     * @return Long
     */
    public static Long getNowTimestampS(){
        return System.currentTimeMillis();
    }

    /**
     * 得到指定字符串的时间戳
     *
     * @param datetimeStr
     * @return
     */
    public static Long getTimestampByStr(String datetimeStr) {
        try {
            Date date;
            if (null == datetimeStr || "".equals(datetimeStr)) {
                date = new Date();
            } else {
                date = sdf_yyyy_MM_dd_HH_mm_ss.get().parse(datetimeStr);
            }
            long ts = date.getTime();
            return ts / 1000;
        } catch (Exception e) {
            throw new MyException("日期转换出错(" + datetimeStr + ")");
        }
    }

    /**
     * 得到指定日期的时间戳
     *
     * @param date 日期
     * @return Long
     */
    public static Long getTimestampByDate(Date date) {
        long ts = date.getTime();
        return ts / 1000;
    }

    /**
     * 将时间戳转成指定格式字符串
     *
     * @param stamp  时间戳
     * @param format 格式
     * @return String
     */
    public static String timestampToStr(int stamp, EnumDateFormat format) {
        SimpleDateFormat sdf = new SimpleDateFormat(format.getValue());
        return sdf.format(new Date((long) stamp * 1000L));
    }

    /**
     * 得到指定年月的最后一天
     *
     * @param yearMonth 2022-01
     * @return 2022-01-31
     */
    public static String getLastDayOfMonth(String yearMonth) {
        int year = Integer.parseInt(yearMonth.split("-")[0]);  //年
        int month = Integer.parseInt(yearMonth.split("-")[1]); //月
        Calendar cal = Calendar.getInstance();
        // 设置年份
        cal.set(Calendar.YEAR, year);
        // 设置月份
        // cal.set(Calendar.MONTH, month - 1);
        cal.set(Calendar.MONTH, month); //设置当前月的上一个月
        // 获取某月最大天数
        //int lastDay = cal.getActualMaximum(Calendar.DATE);
        int lastDay = cal.getMinimum(Calendar.DATE); //获取月份中的最小值，即第一天
        // 设置日历中月份的最大天数
        //cal.set(Calendar.DAY_OF_MONTH, lastDay);
        cal.set(Calendar.DAY_OF_MONTH, lastDay - 1); //上月的第一天减去1就是当月的最后一天
        // 格式化日期
        return sdf_yyyy_MM_dd.get().format(cal.getTime());
    }

    /**
     * 字符串日期比较大小
     *
     * s1和s2相等返回true
     * s1 比 s2 大返回true
     * 其余都返回false
     *
     * @param s1 2022-02-01 10:00
     * @param s2 2022-02-02 11:00
     * @return boolean
     * @throws ParseException p
     */
    public static boolean eqDate(String s1, String s2, EnumDateFormat dateFormat) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat.getValue());
        Date d1 = sdf.parse(s1);
        Date d2 = sdf.parse(s2);
        int i = d1.compareTo(d2);
        return i >= 0;
    }

    /**
     * 传入Date增加时间
     *
     * @param date date 原日期
     * @param i    增加的数量
     * @param type 增加类型，如：Calendar.MINUTE表示分钟
     * @param dateFormat 返回的数据格式
     * @return
     */
    public static String dateAdd(Date date, int i, int type, EnumDateFormat dateFormat) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(type, i);
        return new SimpleDateFormat(dateFormat.getValue()).format(calendar.getTime());
    }

    /**
     * 日期增加，默认日增加，并返回yyyy-MM-dd格式
     * @param date
     * @param i
     * @return
     */
    public static String dateAdd(Date date, int i) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, i);
        return new SimpleDateFormat(EnumDateFormat.YYYY_MM_DD.getValue()).format(calendar.getTime());
    }

    /**
     * 计算二个日期之间的天数
     * @param d1 开始日期
     * @param d2 结束日期
     * @return 天数
     * @throws ParseException E
     */
    public static long getBetweenDayCount(String d1, String d2) throws ParseException {
        Date date1 = UtilsDate.sdf_yyyy_MM_dd.get().parse(d1);
        Date date2 = UtilsDate.sdf_yyyy_MM_dd.get().parse(d2);
        return DateUtil.between(date1, date2, DateUnit.DAY);
    }

    /**
     * 计算二个日期之间的天数
     * @param d1 开始日期
     * @param d2 结束日期
     * @return 天数
     * @throws ParseException E
     */
    public static long getBetweenHourCount(String d1, String d2) throws ParseException {
        Date date1 = UtilsDate.sdf_yyyy_MM_dd.get().parse(d1);
        Date date2 = UtilsDate.sdf_yyyy_MM_dd.get().parse(d2);
        return DateUtil.between(date1, date2, DateUnit.HOUR);
    }

    /**
     * 得到二个日期之间的日期
     * @param d1 开始日期
     * @param d2 结束日期
     * @return set<日期>
     * @throws ParseException E
     */
    public static Set<String> getBetweenDaySet(String d1, String d2) throws ParseException {
        Date date1 = UtilsDate.sdf_yyyy_MM_dd.get().parse(d1);
        long count = getBetweenDayCount(d1, d2);
        Set<String> rs = new TreeSet<>();
        for(int i = 0; i<=count; i++){
            rs.add(UtilsDate.dateAdd(date1, i));
        }
        return rs;
    }

    /**
     * 得到二个日期之间的日期
     * @param d1 开始日期
     * @param d2 结束日期
     * @return set<日期>
     * @throws ParseException E
     */
    public static List<String> getBetweenDayList(String d1, String d2) throws ParseException {
        Date date1 = UtilsDate.sdf_yyyy_MM_dd.get().parse(d1);
        long count = getBetweenDayCount(d1, d2);
        List<String> rs = new ArrayList<>();
        for(int i = 0; i<=count; i++){
            rs.add(UtilsDate.dateAdd(date1, i));
        }
        return rs;
    }

    /**
     * 计算二个时间之前的小时集合
     */
    public static List<Date> getBetweenHourList(String start, String end) throws ParseException {
        Date startDate = UtilsDate.sdf_yyyy_MM_dd_HH_mm_ss.get().parse(start);
        List<Date> rs = new ArrayList<>();
        long count = getBetweenHourCount(start, end);
        for(int i = 0; i<count; i++){
            rs.add(DateUtil.offsetHour(startDate, i));
        }
        return rs;
    }

    /**
     * 得到查询起止时间
     * @param date 查询日期, 不填时为前一天
     */
    public static Map<String, String> getDate(String date){
        String startTime, endTime;
        Map<String, String> rs = new HashMap<>();
        if(null == date || "".equals(date)){
            DateTime yesterday = DateUtil.yesterday();
            startTime = UtilsDate.sdf_yyyy_MM_dd.get().format(yesterday);
            rs.put("date", startTime);
            startTime = startTime + " 08:00:00";
            String today = DateUtil.today();
            endTime = today + " 07:59:59";
        }else{
            rs.put("date", date);
            startTime = date + " 08:00:00";
            DateTime endDay = DateUtil.offsetDay(DateUtil.parseDate(date), 1);
            endTime = UtilsDate.sdf_yyyy_MM_dd.get().format(endDay);
            endTime = endTime + " 07:59:59";
        }
        rs.put("startTime", startTime);
        rs.put("endTime", endTime);
        return rs;
    }

    /**
     * 根据某个带小时分钟的时间, 得到其所属工作日
     */
    public static String getDoDateByDate(Date date){
        //得到日期的所属小时
        int hour = DateUtil.hour(date, true);
        if(hour < 8){
            return UtilsDate.dateAdd(date, -1);
        }
        return UtilsDate.sdf_yyyy_MM_dd.get().format(date);
    }

    public static String getDoDateByStr(String date) throws ParseException {
        Date date1 = UtilsDate.sdf_yyyy_MM_dd_HH_mm_ss.get().parse(date);
        return getDoDateByDate(date1);
    }

    /**
     * 计算二个日期之间的分钟数
     */
    public static Long getAlongByStr(String start, String end) throws ParseException {
        return DateUtil.between(
                sdf_yyyy_MM_dd_HH_mm_ss.get().parse(start),
                sdf_yyyy_MM_dd_HH_mm_ss.get().parse(end),
                DateUnit.MINUTE);
    }

}
